﻿using Sai.Core;
using Sai.Core.Rules;
using Sai.Test.TestDataBuilders;
using Sai.Test.XUnit;
using Xunit;

namespace Sai.Test.Rules
{
    public class CheckTypeConstructorsFacts
    {
        [Fact, TouchingFileSystemTest]
        public void Adding_Explicit_Default_Constructor_Is_Not_Flagged()
        {
            const string className = "Class1";
            const string namespaceName = "ClassLibrary1";

            string oldCode = BoilerPlateCodeBuilder.BuildFor(namespaceName, 
                                CompilableClassBuilder.PublicClass()
                                    .Named(className));

            string newCode = BoilerPlateCodeBuilder.BuildFor(namespaceName,
                                CompilableClassBuilder.PublicClass()
                                    .Named(className)
                                    .WithDefaultConstructor());

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeConstructors())
                .Build();

            Assert.False(diff.HasChanged);

            Assert.Null(diff.ConstructorsAdded);
            Assert.Null(diff.ConstructorsChanged);
            Assert.Null(diff.ConstructorsRemoved);
            Assert.Null(diff.ConstructorsObsolete);
        }

        [Fact, TouchingFileSystemTest]
        public void Removing_Explicit_Default_Constructor_Is_Not_Flagged()
        {
            const string className = "Class1";
            const string namespaceName = "ClassLibrary1";

            string oldCode = BoilerPlateCodeBuilder.BuildFor(namespaceName,
                                    CompilableClassBuilder.PublicClass()
                                        .Named(className)
                                        .WithDefaultConstructor());

            string newCode = BoilerPlateCodeBuilder.BuildFor(namespaceName,
                                    CompilableClassBuilder.PublicClass()
                                        .Named(className));

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeConstructors())
                .Build();

            Assert.False(diff.HasChanged);

            Assert.Null(diff.ConstructorsAdded);
            Assert.Null(diff.ConstructorsChanged);
            Assert.Null(diff.ConstructorsRemoved);
            Assert.Null(diff.ConstructorsObsolete);
        }

        [Fact, TouchingFileSystemTest]
        public void Adding_Constructor_Is_Flagged()
        {
            const string className = "Class1";

            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className);

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className)
                .WithDefaultConstructor()
                .WithConstructor("public Class1(int g) { }");

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeConstructors())
                .Build();

            Assert.True(diff.HasChanged);

            Assert.NotNull(diff.ConstructorsAdded);
            Assert.Equal(1, diff.ConstructorsAdded.Count);

            Assert.Null(diff.ConstructorsChanged);
            Assert.Null(diff.ConstructorsRemoved);
            Assert.Null(diff.ConstructorsObsolete);
        }

        [Fact, TouchingFileSystemTest]
        public void Removing_Constructor_Is_Flagged()
        {
            const string className = "Class1";

            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className)
                .WithDefaultConstructor()
                .WithConstructor("public Class1(int g) { }");

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className)
                .WithDefaultConstructor();

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeConstructors())
                .Build();

            Assert.True(diff.HasChanged);

            Assert.NotNull(diff.ConstructorsRemoved);
            Assert.Equal(1, diff.ConstructorsRemoved.Count);

            Assert.Null(diff.ConstructorsChanged);
            Assert.Null(diff.ConstructorsAdded);
            Assert.Null(diff.ConstructorsObsolete);
        }

        [Fact, TouchingFileSystemTest]
        public void Changing_Constructor_Signature_Is_Flagged()
        {
            const string className = "Class1";

            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className)
                .WithDefaultConstructor()
                .WithConstructor("public Class1(int g) { }");

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className)
                .WithDefaultConstructor()
                .WithConstructor("public Class1(string g) { }");

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeConstructors())
                .Build();

            Assert.True(diff.HasChanged);

            Assert.NotNull(diff.ConstructorsChanged);
            Assert.Equal(1, diff.ConstructorsChanged.Count);
            Assert.Null(diff.ConstructorsAdded);
            Assert.Null(diff.ConstructorsRemoved);
            Assert.Null(diff.ConstructorsObsolete);
        }

        [Fact, TouchingFileSystemTest]
        public void Adding_Static_Constructor_Is_Flagged()
        {
            const string className = "Class1";

            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className);

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className)
                .WithStaticConstructor();

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeConstructors())
                .Build();

            Assert.True(diff.HasChanged);

            Assert.NotNull(diff.ConstructorsAdded);
            Assert.Equal(1, diff.ConstructorsAdded.Count);
            Assert.Null(diff.ConstructorsChanged);
            Assert.Null(diff.ConstructorsRemoved);
            Assert.Null(diff.ConstructorsObsolete);
        }

        [Fact, TouchingFileSystemTest]
        public void Removing_Static_Constructor_Is_Flagged()
        {
            const string className = "Class1";

            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className)
                .WithStaticConstructor();

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className);

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeConstructors())
                .Build();

            Assert.True(diff.HasChanged);

            Assert.Null(diff.ConstructorsAdded);
            Assert.Null(diff.ConstructorsChanged);
            Assert.NotNull(diff.ConstructorsRemoved);
            Assert.Equal(1, diff.ConstructorsRemoved.Count);
            Assert.Null(diff.ConstructorsObsolete);
        }

    }
}
